package com.loong.dilib.core;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.apache.log4j.Logger;

import com.loong.dilib.core.annotation.ApiConfig;
import com.loong.dilib.core.code.HttpMethod;
import com.loong.dilib.core.code.HttpResult;
import com.loong.dilib.support.HttpTool;
import com.loong.dilib.support.JsonTool;
import com.loong.dilib.util.ApiConfigException;
import com.loong.dilib.util.DIConnectException;

/**
 * Api代理实体处理类
 */
public class ApiInterceptor implements MethodInterceptor {

	/** 日志输出对象 */
	private static Logger log = Logger.getLogger(ApiInterceptor.class);

	/** 日志显示设置 **/
	private boolean showLog = false;

	/** 编码方式 */
	private static String CHARSET = "UTF-8";

	/**
	 * @param showLog 日志显示
	 */
	public void setShowLog(boolean showLog) {

		this.showLog = showLog;
	}

	@Override
	public Object intercept(Object object, Method method, Object[] params, MethodProxy methodProxy)
			throws DIConnectException {

		// 获取方法配置信息
		ApiConfig methodAnn = method.getAnnotation(ApiConfig.class);
		if (methodAnn == null)
			throw new ApiConfigException("Mothod annotation error:" + method.getName());
		else if (methodAnn.value().isEmpty())
			throw new ApiConfigException("Mothod url is null:" + method.getName());

		// 获取类配置信息
		ApiConfig classAnn = null;
		for (Class<?> clas : object.getClass().getInterfaces()) {
			classAnn = clas.getAnnotation(ApiConfig.class);
			if (classAnn != null)
				break;
		}

		// 获取访问方式
		HttpMethod httpMethod = getHttpMethod(methodAnn);
		// 获取返回方式
		HttpResult httpResult = getHttpResult(methodAnn);
		// 获取访问路径
		String url = getUrl(classAnn, methodAnn);
		// 组装访问参数
		String param = convertParam(httpMethod, params);

		// 获取数据信息
		String data = null;

		long start = System.currentTimeMillis();
		try {
			data = getHttp(url, param, httpMethod);
		} catch (IOException e) {
			throw new DIConnectException("Connection error:" + method.getName());
		}
		long end = System.currentTimeMillis();

		// 日志打印
		if (showLog) {
			StringBuilder debug = new StringBuilder();
			debug.append("[dilib] call:").append(end - start).append("ms ").append(url).append(" param:").append(param)
					.append(" result:").append(data);
			log.info(debug.toString());
		}

		// 处理返回参数
		if (httpResult == HttpResult.STRING) {
			return data;
		} else if (httpResult == HttpResult.JSON) {
			return JsonTool.fromJson(data, method.getGenericReturnType());
		} else {
			throw new ApiConfigException("This result is not finished:" + method.getName());
		}
	}

	/**
	 * 获取接口数据
	 * 
	 * @param url 访问路径
	 * @param param 访问参数
	 * @param httpMethod 访问类型
	 * @return
	 * @throws IOException
	 */
	private String getHttp(String url, String param, HttpMethod httpMethod) throws IOException {

		if (httpMethod == HttpMethod.GET)
			return HttpTool.getHttpByGet(url, param);
		else if (httpMethod == HttpMethod.JSON)
			return HttpTool.getHttpByPost(url, param, HttpTool.CONTENT_JSON);
		else
			return HttpTool.getHttpByPost(url, param);
	}

	/**
	 * 获取访问类型配置
	 * 
	 * @param methodAnn 方法注释
	 * @return 访问类型
	 */
	private HttpMethod getHttpMethod(ApiConfig methodAnn) {

		return methodAnn.method();
	}

	/**
	 * 获取返回类型配置
	 * 
	 * @param methodAnn 方法注释
	 * @return 返回类型
	 */
	private HttpResult getHttpResult(ApiConfig methodAnn) {

		return methodAnn.result();
	}

	/**
	 * 获取访问路径配置
	 * 
	 * @param classAnn 类注释
	 * @param methodAnn 方法注释
	 * @return 访问路径
	 */
	private String getUrl(ApiConfig classAnn, ApiConfig methodAnn) {

		if (classAnn == null) {
			return methodAnn.value();
		} else {
			String url = classAnn.value();
			if (!url.endsWith(HttpTool.URL_SPLIT)) {
				url = url + HttpTool.URL_SPLIT;
			}
			return url + methodAnn.value();
		}
	}

	/**
	 * 装配访问参数
	 * 
	 * @param type 参数类型
	 * @param params 参数
	 * @return 访问参数字符串
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	private String convertParam(HttpMethod method, Object... params) {

		if (method == HttpMethod.JSON)
			return convertJsonParam(params);
		else
			return convertDefaultParam(params);
	}

	/**
	 * 装配Json类型访问参数
	 * 
	 * @param params 参数
	 * @return 访问参数字符串
	 */
	private String convertJsonParam(Object... params) {

		if (params == null)
			return null;
		else if (params.length == 1)
			return JsonTool.toJson(params[0]);
		return JsonTool.toJson(params);

	}

	/**
	 * 装配默认类型访问参数
	 * 
	 * @param params 参数
	 * @return 访问参数字符串
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	private String convertDefaultParam(Object... params) {

		if (params == null)
			return null;
		StringBuilder paramStr = new StringBuilder();
		for (Object param : params) {
			if (param == null)
				continue;
			Class<?> clas = param.getClass();
			// 获取变量值,遍历父节点
			while (clas != null && !Object.class.equals(clas)) {
				Field[] fields = clas.getDeclaredFields();
				for (Field f : fields) {
					f.setAccessible(true);
					Object value = null;
					try {
						value = f.get(param);
					} catch (IllegalAccessException e) {
						throw new RuntimeException(e);
					}
					if (value != null) {
						try {
							value = URLEncoder.encode(value.toString(), CHARSET);
						} catch (UnsupportedEncodingException e) {
							throw new RuntimeException("charset config error", e);
						}
						paramStr.append(f.getName()).append(HttpTool.PARAM_VALUE_SPLIT).append(value)
								.append(HttpTool.PARAMS_SPLIT);
					}
				}
				clas = clas.getSuperclass();
			}
		}
		if (paramStr.length() != 0)
			return paramStr.substring(0, paramStr.length() - 1);
		return paramStr.toString();
	}
}
